
#region Imported Namespaces
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Data;
using System.Drawing;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

using PHSRAG.Insight21.Context;
using PHSRAG.Insight21.SearchContext;
using AlphaPicker = PHSRAG.WebControls.AlphaPicker;
using Annunciator = PHSRAG.WebControls.Annunciator;
using DataAccess = PHSRAG.Utility.DataAccess;
using Settings = PHSRAG.Utility.Settings;
using Pager = PHSRAG.WebControls.Pager;
using Policy = PHSRAG.Insight21.Policy;
#endregion

namespace PHSRAG.Insight21.SearchSupport
{
	#region Class Picker
	/// <summary>
	/// The Picker class manages the activities of loading and displaying data relevent to one of PI/Lab/COS. The display
	/// takes the form of a DataGrid which allows the user to select a lab by clicking on the LabName column in a row.
	/// Since there could be many rows, the DataGrid supports pagination through the Pager control.
	/// </summary>
	/// <remarks>
	/// <p>The Picker control (given that it is a UserControl) requires a valid ASPX page to host it. It does not care
	/// much about what such a page does, other than to provide it a valid site.
	/// Loading of the data requires some level of understanding of the current security context. For example, the list
	/// of organizations displayed within the context of an Agreement page may not be the same in the context of a
	/// Personnel page.
	/// This is handled by providing the name of the stored procedure to use while loading the organization data. This
	/// name is provided via the QueryString collection (using the name "Loader") by the invoking entity.</p>
	/// <p>Since the only difference (at least from the perspective of the Picker control) between the PI/Lab/COS pick
	/// contexts is the data that gets loaded into the data grid, the Picker control is designed to access the type of
	/// pick it should support. This type is provieed via the QueryString collection (using the name "Type") by the
	/// invoking entity. Valid values for the Type are "PI", "Lab", and "COS".</p>
	/// <p>
	/// The data grid starts off with two columns; the first column is a hidden column which is meant to hold the key
	/// for the entire row, and the second column is a link button column which is used to select a row (this column is
	/// referred to as the "selector" column.</p>
	/// <p>Given that the notion of key and may vary with the picker type, and given that the number of columns and their
	/// content depends on the picker type, these columns are added dynamically during initialization. Specific helper
	/// methods will handle creating such columns and initializing the first two columns with appropriate information.</p>
	/// <p>The Picker makes a strong assumption that the page which hosts it opens up in a separate browser window.
	/// When a lab is selected, it will generate javascript code to close that window. <bold>If for any reason, this control
	/// is hosted within an existing window, beware that you will lost that window after a selection is made!</bold></p>
	/// </remarks>
	/// <seealso>
	/// </seealso>
	public partial class Picker : System.Web.UI.UserControl
	{
		#region Constants
		private const string ClientScriptKey = "clientScript_A0BB452C-E797-4d74-8853-C8A54627F8BF";
		private const string DataKey = "data_A0BB452C-E797-4d74-8853-C8A54627F8BF";
		private const string FilterKey = "filter_A0BB452C-E797-4d74-8853-C8A54627F8BF";
		private const string FilteredDataKey = "filteredData_A0BB452C-E797-4d74-8853-C8A54627F8BF";
		private const string JavascriptViewAllCallback = "ResetPickerState";
        private const string JavascriptWindowTitle = "SetWindowTitle";
		#endregion

		#region Instance Variables (private)
		private Policy.ICachePolicy	cachePolicy;
		private string	pickerFilterColumn;
		private string	pickerLoader;
		private string	pickerType;
		#endregion

		#region Private Properties
		/// <summary>
		/// Get/Set the data used to initialize the AlphaPicker and the data grid. So as to avoid multiple hits on the
		/// database, the retrieved data is cached under a picker-specific key.
		/// </summary>
		private DataTable FilteredData
		{
			get
			{
				DataTable filteredDataTable = Session[PickerKeyForFilteredData] as DataTable;
				return (filteredDataTable == null) ? PickerData : filteredDataTable;
			}
			set
			{
				Session[PickerKeyForFilteredData] = value;
			}
		}

		/// <summary>
		/// Get/Set the data used to initialize the AlphaPicker and the data grid. So as to avoid multiple hits on the
		/// database, the retrieved data is cached under a picker-specific key.
		/// </summary>
		private DataTable PickerData
		{
			get
			{
				DataTable pickerDataTable = Session[PickerKeyForData] as DataTable;
				if (pickerDataTable == null)
				{
					CommonSearchContext searchContext = cachePolicy.GetCachedObject(CommonSearchContext.CacheKey) as CommonSearchContext;
					System.Diagnostics.Trace.Assert(searchContext != null);
					Session[PickerKeyForData] = pickerDataTable = searchContext.GetPickerData(pickerType, pickerLoader);
				}
				return pickerDataTable;
			}
		}

		private string PickerKeyForData
		{
			get { return string.Format("{0}_{1}", pickerType, DataKey); }
		}

		private string PickerKeyForFilteredData
		{
			get { return string.Format("{0}_filtered_{1}", pickerType, DataKey); }
		}
		#endregion

		#region Pagination Event Handlers
		/// <summary>
		/// Handler for the FirstPage event raised by the Pager. Force the data grid to display the first page.
		/// </summary>
		/// <param name="sender">Page control</param>
		/// <param name="e">Arguments associated with the event</param>
		/// <remarks>
		/// If the data grid is already displaying the first page, no further action is taken.
		/// </remarks>
		private void OnFirstPage(object sender, System.Web.UI.WebControls.CommandEventArgs e)
		{
			if (dataGrid.CurrentPageIndex != 0)
			{
				dataGrid.CurrentPageIndex = 0;
				LoadDataGrid();
			}
		}

		/// <summary>
		/// Handler for the LastPage event raised by the Pager. Force the data grid to display the last page.
		/// </summary>
		/// <param name="sender">Page control</param>
		/// <param name="e">Arguments associated with the event</param>
		/// <remarks>
		/// If the data grid is already displaying the last page, no further action is taken.
		/// </remarks>
		private void OnLastPage(object sender, System.Web.UI.WebControls.CommandEventArgs e)
		{
			if (dataGrid.CurrentPageIndex != (pager.PageCount - 1))
			{
				dataGrid.CurrentPageIndex = pager.PageCount - 1;
				LoadDataGrid();
			}
		}

		/// <summary>
		/// Handler for the NextPage event raised by the Pager. Force the data grid to display the next page.
		/// </summary>
		/// <param name="sender">Page control</param>
		/// <param name="e">Arguments associated with the event</param>
		/// <remarks>
		/// If the data grid is already displaying the last page, no further action is taken.
		/// </remarks>
		private void OnNextPage(object sender, System.Web.UI.WebControls.CommandEventArgs e)
		{
			if (dataGrid.CurrentPageIndex < (pager.PageCount - 1))
			{
				dataGrid.CurrentPageIndex++;
				LoadDataGrid();
			}
		}

		/// <summary>
		/// Handler for the PreviousPage event raised by the Pager. Force the data grid to display the previous page.
		/// </summary>
		/// <param name="sender">Page control</param>
		/// <param name="e">Arguments associated with the event</param>
		/// <remarks>
		/// If the data grid is already displaying the first page, no further action is taken.
		/// </remarks>
		private void OnPreviousPage(object sender, System.Web.UI.WebControls.CommandEventArgs e)
		{
			if (dataGrid.CurrentPageIndex > 0)
			{
				dataGrid.CurrentPageIndex--;
				LoadDataGrid();
			}
		}
		#endregion

		#region Overrides
		/// <summary>
		/// Register clientside scripts.
		/// </summary>
		/// <param name="e">Argument associated with the event.</param>
		protected override void OnPreRender(EventArgs e)
		{
			base.OnPreRender(e);
			RegisterClientScripts();
		}
		#endregion

		#region Event Handlers
		/// <summary>
		/// Handler for the event raised by the AlphaPicker when the selection changes. The selection could either be an
		/// alphabet, or the "ViewAlL" option. The CommandName property of the event argument indicates which alphabet was
		/// selected, or an empty string if the ViewAll option was selected. Based on the choice, the data is filtered and
		/// displayed in the data grid.
		/// Contents of the editor control (within the placeholder) will be reset.
		/// </summary>
		/// <param name="sender">Source of the event</param>
		/// <param name="e">Argument associated with the event</param>
		private void OnAlphaChanged(object sender, CommandEventArgs e)
		{
			LoadFilteredData(e.CommandName);
		}

		/// <summary>
		/// Handler for the event raised by the DataGrid when a row is clicked upon.
		/// </summary>
		/// <param name="source">DataGrid control</param>
		/// <param name="e">Arguments associated with the event</param>
		/// <remarks>
		/// The item command is raised when the user clicks on the LastName column for a row. That column contains a
		/// LinkButton whose CommandName is set to "Select". Events raised for any other reason are ignored.
		/// </remarks>
		private void OnItemCommand(object source, System.Web.UI.WebControls.DataGridCommandEventArgs e)
		{
			if (e.CommandName == "Select")
			{
				// Update the current search criteria using the search context.
				// It is CRUCIAL that the updated search criteria be stored back in the search context; failing which
				// later invocation to interrogate the criteria through the search context will NOT show the PI selection.

				CommonSearchContext searchContext = (CommonSearchContext)cachePolicy.GetCachedObject(CommonSearchContext.CacheKey);
				SearchCriteria searchCriteria = searchContext.GetSearchCriteria(Page.Request.Url.ToString());

				string currentValue = e.Item.Cells[0].Text;
				if (searchCriteria[pickerType] != currentValue)
				{
					searchContext.SetPickerSearchCriteria(pickerType, currentValue);
					Session.Remove(PickerKeyForFilteredData);
				}

				// Generate client-side script to set the picker selection in the opener page.

				if (!Page.ClientScript.IsClientScriptBlockRegistered(ClientScriptKey))
				{
					string script = string.Format(
						"<script language=\"javascript\">\n" +
						"window.opener.SetPickerSelection(\"{0}\", \"{1}\");\n" +
						"window.close();\n" +
						"</script>\n", pickerType.ToUpper(), CommonSearchContext.GetPickerDescription(pickerType, currentValue));

					Page.ClientScript.RegisterClientScriptBlock(GetType( ), ClientScriptKey, script);
				}
			}
		}

        /// <summary>
        /// Formats the e-mail column of the PI picker 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnItemDataBound(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e)
        {
            try
            {
                if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
                {
                    if (pickerType == "PI" && e.Item.Cells[2].Text != "&nbsp;")
                        e.Item.Cells[2].Text = string.Format("<a href=\"mailto:{0}\">{0}</a>",
                                                                e.Item.Cells[2].Text);
                }
            }
            catch (Exception ex)
            {
                annunciator.Message = String.Format("{0} while attempting to format columns."
                    ,Utility.ExceptionReport.GetExceptionInfo(ex, false));
            }
        }

		/// <summary>
		/// On first load of the control, initialize the AlphaPicker and load the DataGrid with PI data.
		/// </summary>
		/// <param name="sender">Control being loaded</param>
		/// <param name="e">Argument associated with the event</param>
		protected void OnPageLoad(object sender, System.EventArgs e)
		{
			if (!IsPostBack)
			{
				alphaPicker.Reset();
				alphaPicker.DataSource = PickerData;
				alphaPicker.DataColumn = pickerFilterColumn;
				alphaPicker.JavascriptViewAllCallback = JavascriptViewAllCallback;
				searchLabel.Text = string.Format("Search {0}s", pickerType);
				reset.Text = string.Format("Clear {0}", pickerType);
				dataGrid.PageSize = Settings.GetKeyValue("DataGrid.ItemsPerPage", 10);
				LoadDataGrid();
			}
			
		}

		/// <summary>
		/// Reset the selection state. This is done by removing the current picker value from the search criteria and then
		/// uncaching any associated picker data from the session. 
		/// </summary>
		/// <param name="sender">Control which raised the event</param>
		/// <param name="e">Argument associated with the event</param>
		protected void OnReset(object sender, System.EventArgs e)
		{
			CommonSearchContext searchContext = (CommonSearchContext)cachePolicy.GetCachedObject(CommonSearchContext.CacheKey);
			SearchCriteria searchCriteria = searchContext.GetSearchCriteria(Page.Request.Url.ToString());

			if (searchCriteria != null)
			{
				searchContext.ClearPickerSearchCriteria(pickerType);
				Session.Remove(PickerKeyForFilteredData);
			}
			alphaPicker.Reset();
			dataGrid.Visible = pager.Visible = true;
			LoadDataGrid();
			annunciator.Message = "";
			searchText.Text = "";
			
		}

		/// <summary>
		/// Handler for the Search button. Helps in filtering the data displayed in the data grid.
		/// </summary>
		/// <param name="sender">The control which raised the event</param>
		/// <param name="e">Argument associated with the event</param>
		/// <remarks>
		/// <p>There are two inputs to the search action:</p>
		/// <list type="bullet">
		/// <item><description>An alphabet from the AlphaPicker</description></item>
		/// <item><description>Free-form text typed in the search text box</description></item>
		/// </list>
		/// <p>If any text is explicitly typed in the text box, it will override any alphabet selection.</p>
		/// <p>A search always implies displaying the first page of the data grid.</p>
		/// </remarks>
		protected void OnSearch(object sender, System.EventArgs e)
		{
			string filter = searchText.Text.Trim();
			if (filter.Length == 0)
				filter = alphaPicker.Selection;
			else
				alphaPicker.Selection = filter.Substring(0, 1);

			LoadFilteredData(filter);
		}
		#endregion

		#region Private Methods
		/// <summary>
		/// Create and initialized columns in the data grid for the Lab pick context.
		/// </summary>
		private void CreateLabColumns()
		{
			ButtonColumn selectorColumn = (ButtonColumn)dataGrid.Columns[1];
			selectorColumn.DataTextField = pickerFilterColumn;
			selectorColumn.HeaderText = "Laboratory";

			BoundColumn dataColumn = (BoundColumn)dataGrid.Columns[0];
			dataColumn.DataField="Id";

			dataColumn = new BoundColumn();
			dataColumn.DataField = "LabOwnerName";
			dataColumn.HeaderText = "Owner";
			dataGrid.Columns.Add(dataColumn);
			
			dataColumn = new BoundColumn();
			dataColumn.DataField = "OrganizationName";
			dataColumn.HeaderText = "Organization";
			dataGrid.Columns.Add(dataColumn);
		}

		/// <summary>
		/// Create and initialized columns in the data grid for the PI pick context.
		/// </summary>
		private void CreatePIColumns()
		{
			ButtonColumn selectorColumn = (ButtonColumn)dataGrid.Columns[1];
			selectorColumn.DataTextField = pickerFilterColumn;
			selectorColumn.HeaderText = "PI Name";

			BoundColumn dataColumn = (BoundColumn)dataGrid.Columns[0];
			dataColumn.DataField="Id";

			dataColumn = new BoundColumn();
			dataColumn.DataField = "EMail";
			dataColumn.HeaderText = "E-Mail";
			dataGrid.Columns.Add(dataColumn);
		}

		/// <summary>
		/// Load the DataGrid with PI data. The name of the stored procedure to be used for the load is specified by
		/// the invoking entity via QueryString using the key "DataLoader".
		/// Initialize the Pager based on the volume of data retrieved from the database. The navigation links on the
		/// Pager are also set.
		/// </summary>
		/// <remarks>
		/// The data that is loaded into the data grid is filtered based upon the selection in the alpha picker.
		/// </remarks>
		private void LoadDataGrid()
		{
			DataTable pickerData = FilteredData;
			pager.RecordCount = pickerData.Rows.Count;

			int pageCount = (pager.RecordCount / dataGrid.PageSize) + (int)(((pager.RecordCount % dataGrid.PageSize) == 0) ? 0 : 1);
			pager.PageCount = pageCount;
			pager.CurrentPage = dataGrid.CurrentPageIndex + 1;	// Pager uses 1-based index.

			dataGrid.DataSource = pickerData;
			dataGrid.DataBind();

			alphaPicker.Visible = searchLabel.Visible = searchText.Visible = search.Visible = pager.Visible = (dataGrid.Items.Count > 0);

			if (pager.Visible)
			{
				int startIndex = dataGrid.CurrentPageIndex * dataGrid.PageSize;
				pager.FirstRecord = startIndex + 1;
				pager.LastRecord = startIndex + Math.Min(startIndex + dataGrid.PageSize, dataGrid.Items.Count);

				ManageNavigationLinks();
			}
			else
			{
				UserContext userContext = cachePolicy.GetCachedObject(UserContext.CacheKey) as UserContext;
				System.Diagnostics.Trace.Assert(userContext != null);

				annunciator.Message = string.Format("No {0} data found for {1} {2}.", pickerType,
					userContext.FirstName, userContext.LastName);
				annunciator.Visible = true;
				dataGrid.Visible = false;

				Controls.Add(new LiteralControl(
					"<input type=\"button\" class=\"Button\" value=\"Close Window\" onclick=\"window.close();return false;\">"));
			}
		}

		/// <summary>
		/// Apply the filter criteria to the picker data and load the data grid.
		/// </summary>
		/// <param name="filter">Criteria to filter the data on</param>
		private void LoadFilteredData(string filter)
		{
			DataTable pickerData = PickerData;
			DataRow[] rows = pickerData.Select(string.Format("{0} LIKE '{1}%'", pickerFilterColumn, filter));
			if (rows.Length > 0)
			{
				DataTable filteredData = pickerData.Clone();
				foreach (DataRow row in rows)
					filteredData.ImportRow(row);
				FilteredData = filteredData;

				dataGrid.CurrentPageIndex = 0;
				LoadDataGrid();
				dataGrid.Visible = pager.Visible = true;
			}
			else
			{
				annunciator.Message = string.Format("No {0} data found matching the search criteria.", pickerType);
				annunciator.Visible = true;
				dataGrid.Visible = pager.Visible = false;
			}
		}

		/// <summary>
		/// Set the First/Last/Next/Previous page links on the Pager control to enabled/disabled based on which page is
		/// being displayed in the DataGrid.
		/// The First/Previous links are disabled if the first page is being displayed.
		/// The Last/Next links are disabled if the last page is being displayed.
		/// </summary>
		private void ManageNavigationLinks()
		{
			pager.EnableFirstPage = pager.EnablePreviousPage = (dataGrid.CurrentPageIndex != 0);
			pager.EnableLastPage = pager.EnableNextPage = (dataGrid.CurrentPageIndex != (pager.PageCount - 1));
		}

		private void RegisterClientScripts()
		{
			if (!Page.ClientScript.IsClientScriptBlockRegistered(JavascriptViewAllCallback))
			{
				string script = string.Format(
					"<script language=\"javascript\">\n" +
					"function {0}()\n" +
					"{{\n" +
					"\tvar textbox = document.getElementById('{1}');\n" +
					"\tif (textbox != null)\n" +
					"\t\ttextbox.value = '';\n" +
					"}}\n" +
					"</script>\n", JavascriptViewAllCallback, searchText.ClientID);

				Page.ClientScript.RegisterClientScriptBlock(GetType( ), JavascriptViewAllCallback, script);
			}

            if (!Page.ClientScript.IsClientScriptBlockRegistered(JavascriptWindowTitle))
            {
                string script2 = string.Format(
                    "<script language=\"javascript\">\n" +
                    "\twindow.pageTitle.text = \"{0} Search\";\n" +
                    "</script>\n", pickerType);

                Page.ClientScript.RegisterClientScriptBlock(GetType( ), JavascriptWindowTitle, script2);
            }
		}
		#endregion

		#region Web Form Designer generated code
		/// <summary>
		/// Initialize the control. Create dynamic columns in the data grid based on the pick type.
		/// </summary>
		/// <param name="e">Argument associated with the event</param>
		override protected void OnInit(EventArgs e)
		{
			//
			// CODEGEN: This call is required by the ASP.NET Web Form Designer.
			//
			InitializeComponent();
			base.OnInit(e);

			cachePolicy = (Policy.ICachePolicy)Page.Session[Policy.Names.CachePolicyKey];
			pickerFilterColumn = Request.QueryString["FilterColumn"];
			pickerType = Request.QueryString["Type"];
			pickerLoader = Request.QueryString["Loader"];
			string currentPickerType = pickerType.ToUpper();

			if (currentPickerType == CommonSearchContext.LabPickerKey)
				CreateLabColumns();
			else if (currentPickerType == CommonSearchContext.PIPickerKey)
				CreatePIColumns();
			else
				throw new Exception(string.Format("{0} is not a supported picker type", pickerType));

			searchText.Attributes["onchange"] = string.Format("{0}();", AlphaPicker.JavascriptResetFunction);
		}
		
		/// <summary>
		///		Required method for Designer support - do not modify
		///		the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			this.alphaPicker.AlphaChanged += new CommandEventHandler(OnAlphaChanged);
			this.pager.FirstPage += new System.Web.UI.WebControls.CommandEventHandler(this.OnFirstPage);
			this.pager.PreviousPage += new System.Web.UI.WebControls.CommandEventHandler(this.OnPreviousPage);
			this.pager.NextPage += new System.Web.UI.WebControls.CommandEventHandler(this.OnNextPage);
			this.pager.LastPage += new System.Web.UI.WebControls.CommandEventHandler(this.OnLastPage);
			this.dataGrid.ItemCommand += new System.Web.UI.WebControls.DataGridCommandEventHandler(this.OnItemCommand);
            this.dataGrid.ItemDataBound += new System.Web.UI.WebControls.DataGridItemEventHandler(this.OnItemDataBound);
            this.Load += new System.EventHandler(this.OnPageLoad);

		}
		#endregion
	}
	#endregion
}

// --- EOF ---
